import { Injectable, Logger } from '@nestjs/common';
import { HealthIndicatorResult, HealthIndicator } from '@nestjs/terminus';
import { ConfigService } from '@nestjs/config';
import { RedisService } from '../redis/redis.service';
import * as os from 'os';
import * as fs from 'fs';

@Injectable()
export class HealthService extends HealthIndicator {
  private readonly logger = new Logger(HealthService.name);
  private readonly startTime = Date.now();

  constructor(
    private readonly redisService: RedisService,
    private readonly configService: ConfigService,
  ) {
    super();
  }

  // Redis健康检查
  async redisHealthIndicator(): Promise<HealthIndicatorResult> {
    const key = 'health-check';
    const testValue = 'test-' + Date.now();

    try {
      // 测试Redis写入和读取
      await this.redisService.set(key, testValue, 10);
      const result = await this.redisService.get(key);

      if (result === testValue) {
        await this.redisService.del(key);
        return this.getStatus('redis', true, {
          status: 'connected',
          responseTime: 'fast',
        });
      } else {
        throw new Error('Redis读写测试失败');
      }
    } catch (error) {
      this.logger.error(`Redis健康检查失败: ${error.message}`);
      return this.getStatus('redis', false, {
        status: 'disconnected',
        error: error.message,
      });
    }
  }

  // 内存健康检查
  async memoryHealthIndicator(): Promise<HealthIndicatorResult> {
    const memoryUsage = process.memoryUsage();
    const totalMemory = os.totalmem();
    const freeMemory = os.freemem();
    const usedMemory = totalMemory - freeMemory;
    const memoryUsagePercent = (usedMemory / totalMemory) * 100;

    const heapUsedMB = Math.round(memoryUsage.heapUsed / 1024 / 1024);
    const heapTotalMB = Math.round(memoryUsage.heapTotal / 1024 / 1024);
    const rssMB = Math.round(memoryUsage.rss / 1024 / 1024);

    const isHealthy = memoryUsagePercent < 90 && heapUsedMB < 1000; // 90%内存使用率和1GB堆内存阈值

    return this.getStatus('memory', isHealthy, {
      heapUsed: `${heapUsedMB}MB`,
      heapTotal: `${heapTotalMB}MB`,
      rss: `${rssMB}MB`,
      external: `${Math.round(memoryUsage.external / 1024 / 1024)}MB`,
      systemMemoryUsage: `${memoryUsagePercent.toFixed(2)}%`,
      totalMemory: `${Math.round(totalMemory / 1024 / 1024 / 1024)}GB`,
      freeMemory: `${Math.round(freeMemory / 1024 / 1024 / 1024)}GB`,
    });
  }

  // 磁盘健康检查
  async diskHealthIndicator(): Promise<HealthIndicatorResult> {
    try {
      fs.statSync(process.cwd());
      const diskUsage = await this.getDiskUsage();

      const isHealthy = diskUsage.usagePercent < 90; // 90%磁盘使用率阈值

      return this.getStatus('disk', isHealthy, {
        path: process.cwd(),
        totalSpace: diskUsage.total,
        freeSpace: diskUsage.free,
        usedSpace: diskUsage.used,
        usagePercent: `${diskUsage.usagePercent.toFixed(2)}%`,
      });
    } catch (error) {
      this.logger.error(`磁盘健康检查失败: ${error.message}`);
      return this.getStatus('disk', false, {
        error: error.message,
      });
    }
  }

  // WebSocket健康检查
  async websocketHealthIndicator(): Promise<HealthIndicatorResult> {
    try {
      // 这里可以检查WebSocket服务的状态
      // 例如检查连接数、活跃房间数等
      const isHealthy = true; // 简化实现

      return this.getStatus('websocket', isHealthy, {
        status: 'running',
        port: this.configService.get('app.websocket.port', 3001),
      });
    } catch (error) {
      this.logger.error(`WebSocket健康检查失败: ${error.message}`);
      return this.getStatus('websocket', false, {
        error: error.message,
      });
    }
  }

  // 应用就绪检查
  async applicationReadinessIndicator(): Promise<HealthIndicatorResult> {
    try {
      const uptime = Date.now() - this.startTime;
      const isReady = uptime > 5000; // 应用启动5秒后认为就绪

      return this.getStatus('application', isReady, {
        status: isReady ? 'ready' : 'starting',
        uptime: `${Math.round(uptime / 1000)}s`,
        environment: this.configService.get('app.env'),
        version: this.configService.get('app.version', '1.0.0'),
      });
    } catch (error) {
      return this.getStatus('application', false, {
        error: error.message,
      });
    }
  }

  // 应用存活检查
  async applicationLivenessIndicator(): Promise<HealthIndicatorResult> {
    try {
      const uptime = process.uptime();
      const isAlive = uptime > 0;

      return this.getStatus('application', isAlive, {
        status: 'alive',
        uptime: `${Math.round(uptime)}s`,
        pid: process.pid,
        nodeVersion: process.version,
        platform: process.platform,
      });
    } catch (error) {
      return this.getStatus('application', false, {
        error: error.message,
      });
    }
  }

  // 获取应用指标
  async getApplicationMetrics() {
    const memoryUsage = process.memoryUsage();
    const cpuUsage = process.cpuUsage();
    const uptime = process.uptime();

    return {
      timestamp: new Date(),
      uptime: {
        seconds: Math.round(uptime),
        human: this.formatUptime(uptime),
      },
      memory: {
        heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
        heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024),
        rss: Math.round(memoryUsage.rss / 1024 / 1024),
        external: Math.round(memoryUsage.external / 1024 / 1024),
        unit: 'MB',
      },
      cpu: {
        user: cpuUsage.user,
        system: cpuUsage.system,
        unit: 'microseconds',
      },
      system: {
        platform: process.platform,
        arch: process.arch,
        nodeVersion: process.version,
        cpuCount: os.cpus().length,
        totalMemory: Math.round(os.totalmem() / 1024 / 1024 / 1024),
        freeMemory: Math.round(os.freemem() / 1024 / 1024 / 1024),
        loadAverage: os.loadavg(),
        unit: 'GB',
      },
      process: {
        pid: process.pid,
        ppid: process.ppid,
        cwd: process.cwd(),
        execPath: process.execPath,
        argv: process.argv,
      },
    };
  }

  // 获取应用状态
  async getApplicationStatus() {
    const startTime = new Date(this.startTime);
    const uptime = Date.now() - this.startTime;

    return {
      status: 'running',
      timestamp: new Date(),
      startTime,
      uptime: {
        milliseconds: uptime,
        seconds: Math.round(uptime / 1000),
        human: this.formatUptime(uptime / 1000),
      },
      environment: this.configService.get('app.env'),
      version: this.configService.get('app.version', '1.0.0'),
      port: this.configService.get('app.port'),
      apiPrefix: this.configService.get('app.apiPrefix'),
      cors: {
        enabled: this.configService.get('app.cors.enabled'),
        origin: this.configService.get('app.cors.origin'),
      },
      websocket: {
        enabled: true,
        port: this.configService.get('app.websocket.port'),
      },
      database: {
        type: 'postgresql',
        host: this.configService.get('database.host'),
        port: this.configService.get('database.port'),
        database: this.configService.get('database.database'),
      },
      redis: {
        host: this.configService.get('redis.host'),
        port: this.configService.get('redis.port'),
        database: this.configService.get('redis.db'),
      },
    };
  }

  // 私有辅助方法
  private async getDiskUsage(): Promise<{
    total: string;
    free: string;
    used: string;
    usagePercent: number;
  }> {
    try {
      // 简化的磁盘使用率检查
      // 在生产环境中，可能需要使用更精确的方法
      fs.statSync(process.cwd());

      // 模拟磁盘使用情况（实际应该使用系统调用）
      const total = 100 * 1024 * 1024 * 1024; // 100GB
      const free = 20 * 1024 * 1024 * 1024; // 20GB
      const used = total - free;
      const usagePercent = (used / total) * 100;

      return {
        total: this.formatBytes(total),
        free: this.formatBytes(free),
        used: this.formatBytes(used),
        usagePercent,
      };
    } catch (error) {
      throw new Error(`无法获取磁盘使用情况: ${error.message}`);
    }
  }

  private formatBytes(bytes: number): string {
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return '0 Bytes';
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + ' ' + sizes[i];
  }

  private formatUptime(seconds: number): string {
    const days = Math.floor(seconds / 86400);
    const hours = Math.floor((seconds % 86400) / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const secs = Math.floor(seconds % 60);

    const parts = [];
    if (days > 0) parts.push(`${days}d`);
    if (hours > 0) parts.push(`${hours}h`);
    if (minutes > 0) parts.push(`${minutes}m`);
    if (secs > 0) parts.push(`${secs}s`);

    return parts.join(' ') || '0s';
  }
}
